
#include "CTakenPriceMap.h"

#include <uam/UserAccountManager.h>

#include "../BondCDCPriceInfo.h"
#include "../BondMarketStream.h"
#include "../QBSortVContainer.h"

#ifndef _MSC_VER
#define RTL_FIELD_SIZE(type, field) (sizeof(((type*)0)->field))
#endif

#define MAX_VAL 1E12

namespace {
static std::vector<std::string> _mapBroker;
static bool _brokerInited = false;

void _initBroker() {
  if (!_brokerInited) {
    const CBrokerList::BrokerMap& constMap =
        SSUserAccountManager::instance().GetReadonlyBroker().GetBrokerMap();
    for (CBrokerList::BrokerMap::const_iterator it = constMap.begin();
         it != constMap.end(); ++it) {
      char sBrokerID[33] = {0};
      FMTBUF(sBrokerID, "%d", it->first);
      _mapBroker.push_back(sBrokerID);
    }
    _brokerInited = true;
  }
}
}  // namespace

CTakenPriceMap::CTakenPriceMap() : m_min_value(-1E5), m_with_yieldstr(false) {}
void CTakenPriceMap::Clear() { m_prices.clear(); }
namespace {
int CompanyIdToBrokerId(const char* companyId) {
  int id = atoi(companyId);
  if (CBrokerList::IsValidBroker(id)) return id;
  char c = companyId[0];
  int corpid = c;
  return corpid;
}
}  // namespace
void CTakenPriceMap::SetMap(int ncorpid, bool withString) {
  // 优化:避免GetLastDayMSByCorp的临时list
  m_prices.clear();
  m_clear_prices.clear();
  m_yield_prices.clear();
  int unDay = CMarketStream::GetBondDealInvalidateDay();

  FOR_EACH_MARKETSTREAM(unDay, it) {
    const MarketStreamInfo& msi = *it;
    int corpid = CompanyIdToBrokerId(msi.m_company_id);
    if ((ncorpid == 0 && CBrokerList::IsValidBroker(corpid)) ||
        ncorpid == corpid)
      SetTakenPrice(*it, withString);
  }
}
const char* CTakenPriceMap::GetClearPriceStr(const BrokerKey& key) {
  static const char* blank = "";
  if (atoi(key.GetBrokerId().c_str()) == 0) {
    _initBroker();
    time_t lastUpdate = 0;
    ClearPriceMap::iterator itFound = m_clear_prices.end();
    for (std::vector<std::string>::const_iterator it = _mapBroker.begin();
         it != _mapBroker.end(); it++) {
      BrokerKey tempKey(*it, key.m_nIndex);
      YieldPriceMap::iterator it2 = m_clear_prices.find(tempKey);
      if (it2 != m_clear_prices.end()) {
        if (it2->second.m_time > lastUpdate) {
          lastUpdate = it2->second.m_time;
          itFound = it2;
        }
      }
    }
    if (itFound == m_clear_prices.end()) return blank;
    return itFound->second.m_price;
  }
  ClearPriceMap::iterator it = m_clear_prices.find(key);
  return it != m_clear_prices.end() ? it->second.m_price : blank;
}
const char* CTakenPriceMap::GetYeildStr(const BrokerKey& key) {
  static const char* blank = "";

  if (atoi(key.GetBrokerId().c_str()) == 0) {
    _initBroker();
    time_t lastUpdate = 0;
    YieldPriceMap::iterator itFound = m_yield_prices.end();
    for (std::vector<std::string>::const_iterator it = _mapBroker.begin();
         it != _mapBroker.end(); it++) {
      BrokerKey tempKey(*it, key.m_nIndex);
      YieldPriceMap::iterator it2 = m_yield_prices.find(tempKey);
      if (it2 != m_yield_prices.end()) {
        if (it2->second.m_time > lastUpdate) {
          lastUpdate = it2->second.m_time;
          itFound = it2;
        }
      }
    }
    if (itFound == m_yield_prices.end()) return blank;
    return itFound->second.m_price;
  }

  YieldPriceMap::iterator it = m_yield_prices.find(key);
  return it != m_yield_prices.end() ? it->second.m_price : blank;
}
double CTakenPriceMap::GetCleanPrice(const BrokerKey& key, bool bBestQuotePage,
                                     bool bAscend) {
  if (atoi(key.GetBrokerId().c_str()) == 0) {
    _initBroker();
    TakenPrice* ptkn = GetLastTaken(key);
    if (bBestQuotePage)
      return ptkn ? ptkn->price : (bAscend ? MAX_VAL : 0);
    else
      return ptkn ? ptkn->price : 0;
  }
  PriceMap::iterator it = m_prices.find(key);
  if (bBestQuotePage)
    return it != m_prices.end() ? it->second.price : (bAscend ? MAX_VAL : 0);
  else
    return it != m_prices.end() ? it->second.price : 0;
}

double CTakenPriceMap::GetYield(const BrokerKey& key) {
  if (atoi(key.GetBrokerId().c_str()) == 0) {
    _initBroker();
    TakenPrice* ptkn = GetLastTaken(key);
    return ptkn ? ptkn->yield : 0;
  }

  PriceMap::iterator it = m_prices.find(key);
  return it != m_prices.end() ? it->second.yield : 0;
}

double CTakenPriceMap::GetCleanPriceDiv(const BrokerKey& key) {
  if (atoi(key.GetBrokerId().c_str()) == 0) {
    _initBroker();
    TakenPrice* ptkn = GetLastTaken(key);
    return ptkn ? ptkn->clean_price_div : 0;
  }

  PriceMap::iterator it = m_prices.find(key);
  return it != m_prices.end() ? it->second.clean_price_div : 0;
}

double CTakenPriceMap::GetYieldDiv(const BrokerKey& key) {
  if (atoi(key.GetBrokerId().c_str()) == 0) {
    _initBroker();
    TakenPrice* ptkn = GetLastTaken(key);
    return ptkn ? ptkn->yield_div : 0;
  }

  PriceMap::iterator it = m_prices.find(key);
  return it != m_prices.end() ? it->second.yield_div : 0;
}

int CTakenPriceMap::GetExec(const BrokerKey& key) {
  if (atoi(key.GetBrokerId().c_str()) == 0) {
    _initBroker();
    TakenPrice* ptkn = GetLastTaken(key);
    return ptkn ? ptkn->exec : -1;
  }

  PriceMap::iterator it = m_prices.find(key);
  return it != m_prices.end() ? it->second.exec : -1;
}

namespace impl {
const char* GetYield(const MarketStreamInfo& info, const CBondInfo* pBondInfo) {
  if (atoi(info.m_body.m_quotetype) == 3) return info.m_body.m_yield;

  if (pBondInfo->IsBondABS() || pBondInfo->IsTransBond()) {
    double ft = atof(info.m_body.m_yield);
    if (ft <= 0 || ft >= 30) return "";
  }
  return info.m_body.m_yield;
}
};  // namespace impl
bool CTakenPriceMap::SetTakenPrice(const MarketStreamInfo& msi,
                                   bool withString) {
  // see:CSSSortBondInfo::UpdateTknPrice
  int nBondIndex = CBondContainer::instance().GetBondIndex(
      msi.m_body.m_bondkey, msi.m_body.m_listedmarket);
  if (!CBondContainer::instance().IsValidIndex(nBondIndex)) return false;
  const CBondInfo& bi = CBondContainer::instance().ElementAtR(nBondIndex);

  BrokerKey bkey(msi.m_company_id, nBondIndex);
  if (withString) {
    ClearPrice cp = {0};
    memset(&cp, 0, sizeof(cp));
    memcpy(cp.m_price, msi.m_body.m_price,
           RTL_FIELD_SIZE(MarketStreamType2Body, m_price));
    if (m_clear_prices.find(bkey) != m_clear_prices.end()) return true;
    m_clear_prices[bkey] = cp;
    return true;
  }

  if (m_prices.find(bkey) != m_prices.end()) return true;
  TakenPrice tkprice = {0};
  tkprice.price = atof(msi.m_body.m_price);  // 此版本调整显示成交
  tkprice.yield = atof(msi.m_body.m_yield);
  tkprice.clean_price_div = tkprice.yield_div = 0;
  bool bIsCDCAuthValid = bi.IsCDCAuthValid();
  const char* pszPriceDiv = ::GetCleanPriceDiv(
      msi.m_body.m_cleanPrice,
      CBondCDCPriceInfo::instance()
          .GetRelaEstimatePrice(bIsCDCAuthValid, nBondIndex,
                                CBondCDCPriceInfo::price_type_clean,
                                msi.m_body.m_cleanPrice)
          .c_str());
  if (pszPriceDiv != nullptr && strcmp(pszPriceDiv, "--") == 0) {
    tkprice.clean_price_div = m_min_value;
  } else {
    tkprice.clean_price_div = atof(pszPriceDiv);
  }

  const char* pszYieldDiv = ::GetYieldDiv(
      CBondCDCPriceInfo::instance()
          .GetRelaEstimatePrice(bIsCDCAuthValid, nBondIndex,
                                CBondCDCPriceInfo::price_type_yield,
                                msi.m_body.m_yield)
          .c_str(),
      msi.m_body.m_yield);
  if (pszYieldDiv != nullptr && strcmp(pszYieldDiv, "--") == 0) {
    tkprice.yield_div = m_min_value;
  } else {
    tkprice.yield_div = atof(pszYieldDiv);
  }

  m_prices[bkey] = tkprice;

  if (m_with_yieldstr) {
    // 见CDlgQuoteV2::GetTakenPriceStr
    if (m_yield_prices.find(bkey) == m_yield_prices.end()) {
      ClearPrice cp = {0};
      memset(&cp, 0, sizeof(cp));
      FIELDCOPY(cp.m_price, impl::GetYield(msi, &bi));
      m_yield_prices[bkey] = cp;
    }
  }
  return true;
}

double CTakenPriceMap::GetYieldCalc(const BrokerKey& key) {
  PriceMap::iterator it = m_prices.find(key);
  if (it == m_prices.end()) return 0;
  double tkn = it->second.yield;
  const CBondInfo& bi = CBondContainer::instance().ElementAtR(key.m_nIndex);
  if (bi.IsBondABS() || bi.IsTransBond()) {
    if (tkn <= 0 || tkn >= 30) return 0;
  }
  return tkn;
}

CTakenPriceMap::TakenPrice* CTakenPriceMap::GetLastTaken(const BrokerKey& key) {
  time_t lastUpdate = 0;
  PriceMap::iterator itFound = m_prices.end();
  for (std::vector<std::string>::const_iterator it = _mapBroker.begin();
       it != _mapBroker.end(); it++) {
    BrokerKey tempKey(*it, key.m_nIndex);
    PriceMap::iterator it2 = m_prices.find(tempKey);
    if (it2 != m_prices.end()) {
      if (it2->second.time > lastUpdate) {
        lastUpdate = it2->second.time;
        itFound = it2;
      }
    }
  }
  if (itFound == m_prices.end()) return NULL;
  return &(itFound->second);
}
